#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/ipc.h>
#include <sys/sem.h>

/* Note */
/* 使用ipcs -s查看系统目前的信号量 */
/* 使用ipcrm -s semid删除信号量 */
class CSEM
{
private:
  union semun
    {
        int val;
        struct semid_ds *buf;
        unsigned short *array;
  };

  int sem_id;

public:
  bool init(key_t key);
  bool wait();
  bool post();
  bool destroy();
};

int main(int argc, char *argv[])
{
  CSEM sem;
  if (sem.init(0x5000) == false) {
    printf("sem.init failed.\n");
    return -1;
  }
  printf("sem.init success.\n");

  // 等待信号挂出，等待成功后，将持有锁
  if (sem.wait() == false)
  {
    printf("sem.wait failed.\n");
    return -1;
  }
  printf("sem.wait success.\n");

  sleep(50);  // 模拟持有锁后的操作

  // 释放锁
  if (sem.post() == false) {
    printf("sem.post failed.\n");
    return -1;
  }
  printf("sem.post success.\n");

  // 销毁信号灯
  // if (sem.destroy() == false) {
  //   printf("sem.destroy failed.\n");
  //   return -1;
  // }
  // printf("sem.destroy success.\n");
}

bool CSEM::init(key_t key)
{
  if ((sem_id = semget(key, 1, 0640)) == -1) {
    //如果信号灯不存在，则创建信号灯
    if (errno == 2)
    {
        if( (sem_id = semget(key, 1, IPC_CREAT | 0640)) == -1)
        {
            perror("init 1 semget()");
            return false;
        }

        // 信号灯创建成功后，初始化信号灯的值（初始化成可用状态）
        union semun sem_union;
        sem_union.val = 1;
        if (semctl(sem_id, 0, SETVAL, sem_union) < 0)
        {
            perror("init semctl()");
            return false;
        }
    }
    else {
      perror("init 2 semget()");
      return false;
    }
  }
  return true;
}

bool CSEM::wait()
{
  struct sembuf sem_b;
  sem_b.sem_num = 0;
  sem_b.sem_op = -1; //P操作
  sem_b.sem_flg = SEM_UNDO;
  if (semop(sem_id, &sem_b, 1) == -1) {
    perror("wait semop()");
    return false;
  }
  return true;
}

bool CSEM::post()
{
  struct sembuf sem_b;
  sem_b.sem_num = 0;
  sem_b.sem_op = 1; //V操作
  sem_b.sem_flg = SEM_UNDO;
  if (semop(sem_id, &sem_b, 1) == -1) {
    perror("post semop()");
    return false;
  }
  return true;
}

bool CSEM::destroy()
{
  if (semctl(sem_id, 0, IPC_RMID) == -1) {
    perror("destroy semctl()");
    return false;
  }
  return true;
}